﻿using Fizzler.Systems.HtmlAgilityPack;
using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Policy;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
using static System.Windows.Forms.LinkLabel;

namespace HuaJuanAudioNevolDown
{
    public class Extra70tsCom : IExtractor
    {
        private static readonly HttpClient client = new HttpClient();

        string user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0";

        public string Site => "https://www.70ts.com";
        string siteSearchPath = "/so/search.html";
        int htmlRequestDelay = 100;
        public DataTable _novelData { get; set; }
        public ToolStripProgressBar _pbRequestProcess { get; set; }
        public string NovelName { get; set; }
        public string NovelRealHost { get; set; }
        public Form _form { get; set; }
        public async Task ExtractAudioLinksAsync(string novelName, string url)
        {
            this.NovelName = novelName;

            int index = 0;
            // 取得域名网址
            string host = Utils.GetHost(url);

            // 取得root doc node
            var docNode = GetDocumentNode(url);
            List<PageInfo> pages = GetPages(docNode);

            _pbRequestProcess.Minimum = 0;
            _pbRequestProcess.Maximum = pages.Count;
            // 循环每一页
            for (int i = 0; i < pages.Count; i++)
            {
                var page = pages[i];
                var pageUrl = Site + page.Link;
                var chapterNode = GetDocumentNode(pageUrl);
                var chapters = GetChapterLinks(chapterNode);

                for (int j = 0; j < chapters.Count; j++)
                {
                    index++;
                    var info = chapters[j];
                    UpdateListView(index, page.Name, info.AudioName, info.ReqAudioUrl, info.RealAudioUrl, ExtractorStatus.Waiting.GetDescription());
                }
                _pbRequestProcess.Value = i + 1;
                await Task.Delay(htmlRequestDelay);
            }

            _pbRequestProcess.Minimum = 0;
            _pbRequestProcess.Maximum = _novelData.Rows.Count;

            // 用于存放和控制并发任务的列表
            List<Task> taskList = new List<Task>();
            int maxConcurrentTasks = 10;

            for (int j = 0; j < _novelData.Rows.Count; j++)
            {
                DataRow row = _novelData.Rows[j];
                string realUrl = "";

                if (taskList.Count >= maxConcurrentTasks)
                {
                    Task completedTask = await Task.WhenAny(taskList);
                    taskList.Remove(completedTask);

                    UpdateProgressBar(j + 1);
                }

                var task = Task.Run(async () =>
                {
                    for (int k = 0; k < 3; k++)
                    {
                        realUrl = await GetMp3UrlAsync(row["ReqAddress"].ToString());

                        lock (_novelData)
                        {
                            if (realUrl == "")
                            {
                                row["ExtractorStatus"] = ExtractorStatus.Failure.GetDescription();
                                continue;
                            }
                            else
                            {
                                row["RealAddress"] = Uri.EscapeUriString(NovelRealHost + realUrl);
                                row["ExtractorStatus"] = ExtractorStatus.Sucess.GetDescription();
                                break;
                            }
                        }
                    }
                    _pbRequestProcess.Value = j + 1;
                });

                taskList.Add(task);
            }
            await Task.WhenAll(taskList);
            UpdateProgressBar(_novelData.Rows.Count);
        }

        private void UpdateProgressBar(int value)
        {
            if (_form.InvokeRequired)
            {
                _form.Invoke(new Action(() => _pbRequestProcess.Value = value));
            }
            else
            {
                _pbRequestProcess.Value = value;
            }
        }

        public async Task ReExtractAudioLinksAsync(DataGridViewSelectedRowCollection SelectedRows)
        {
            for (int j = 0; j < SelectedRows.Count; j++)
            {
                DataGridViewRow row = SelectedRows[j];

                string realUrl = "";
                if (row.Cells["ExtractorStatus"].Value.ToString() != ExtractorStatus.Sucess.GetDescription())
                {
                    // 正常一次+重试两次，共计3次
                    for (int k = 0; k < 3; k++)
                    {
                        realUrl = await GetMp3UrlAsync(row.Cells["ReqAddress"].Value.ToString());
                        if (realUrl == "")
                        {
                            row.Cells["ExtractorStatus"].Value = ExtractorStatus.Failure.GetDescription();
                            continue;
                        }
                        else
                        {
                            row.Cells["RealAddress"].Value = Uri.EscapeUriString(NovelRealHost + realUrl);
                            row.Cells["ExtractorStatus"].Value = ExtractorStatus.Sucess.GetDescription();
                            break;
                        }
                    }
                }
                await Task.Delay(htmlRequestDelay);
            }
        }

        public async Task<List<SearchResultInfo>> Search(string searchtype, string searchword)
        {
            List<SearchResultInfo> searchResultInfos = new List<SearchResultInfo>();
            client.DefaultRequestHeaders.UserAgent.ParseAdd(user_agent);
            client.DefaultRequestHeaders.Referrer = new Uri(Site);
            searchtype = "novelname";
            var postData = new Dictionary<string, string>
            {
                { "searchtype", searchtype },
                { "searchword", searchword}
            };

            // 使用 FormUrlEncodedContent 发送表单数据
            var content = new FormUrlEncodedContent(postData);
            var url = $"{Site}{siteSearchPath}";
            // 发送 POST 请求
            HttpResponseMessage response = await client.PostAsync(url, content);

            // 确认请求是否成功
            if (response.IsSuccessStatusCode)
            {
                string responseContent = await response.Content.ReadAsStringAsync();

                // 取得root doc node
                var docNode = GetDocumentNodeWithHtml(responseContent);
                var resultListNode = docNode.QuerySelectorAll(".list-works-dl").ToList();

                if (resultListNode.Count > 0)
                {
                    for (int i = 0; i < resultListNode.Count; i++)
                    {
                        var node = resultListNode[i];

                        // 提取书名和链接
                        var titleNode = node.QuerySelector(".list-book-dt a");
                        string novelName = titleNode.InnerText.Trim();
                        string novelAddress = titleNode.GetAttributeValue("href", "").Trim();

                        var nodeList = node.QuerySelectorAll(".list-book-cs span").ToList();
                        // 提取作者
                        string novelAuthor = nodeList[0].InnerText.Trim();
                        novelAuthor = novelAuthor.Replace("作者：", "").Trim();

                        // 提取主播
                        string NovelAnchor = nodeList[1].InnerText.Trim();
                        NovelAnchor = NovelAnchor.Replace("演播：", "");

                        // 提取简介
                        var introNode = node.QuerySelector(".list-book-des");
                        string intro = introNode.InnerText.Trim();

                        searchResultInfos.Add(new SearchResultInfo()
                        {
                            Site = this.Site,
                            NovelName = novelName,
                            NovelAuthor = novelAuthor,
                            NovelAnchor = NovelAnchor,
                            NovelAddress = Site + novelAddress,
                            Novelintro = intro,
                        });
                        await Task.Delay(1);
                    }
                }
            }
            return searchResultInfos;
        }

        private HtmlNode GetDocumentNodeWithHtml(string html)
        {
            // 创建 HtmlDocument 实例
            var doc = new HtmlAgilityPack.HtmlDocument();

            // 加载 HTML 字符串
            doc.LoadHtml(html);

            // 返回根节点（document.DocumentNode）
            return doc.DocumentNode;
        }

        private HtmlNode GetDocumentNode(string url)
        {
            var web = new HtmlWeb();
            var doc = web.Load(url);
            return doc.DocumentNode;
        }

        private List<PageInfo> GetPages(HtmlNode pageNode)
        {
            var optionNodes = pageNode.QuerySelectorAll(".hd-sel select option").ToList();
            List<PageInfo> pages = new List<PageInfo>();
            foreach (var option in optionNodes)
            {
                // 获取每个 <option> 的 value 属性
                var link = option.GetAttributeValue("value", string.Empty);
                if (!link.EndsWith("html"))
                {
                    link += "p1.html";
                }
                var name = option.InnerText.Trim();
                pages.Add(new PageInfo() { Name = name, Link = link });
            }
            return pages;
        }

        private List<ChapterInfo> GetChapterLinks(HtmlNode chapterNode)
        {
            List<ChapterInfo> Mp3InfoList = new List<ChapterInfo>();
            List<HtmlNode> item = chapterNode.QuerySelectorAll("#playlist li a").ToList();

            for (int i = 0; i < item.Count; i++)
            {
                var node = item[i];
                string link = node.GetAttributeValue("href", "");
                string name = node.InnerText;
                name = Regex.Replace(name, "\r\n", "");
                name = name.Trim();
                Mp3InfoList.Add(new ChapterInfo()
                {
                    ReqAudioUrl = Site + link,
                    AudioName = name
                });
            }
            return Mp3InfoList;
        }

        private void UpdateListView(int index, string chatper, string chapterName, string reqUrl, string mp3Url, string status)
        {
            DataRow row = _novelData.NewRow();
            row["Index"] = index.ToString();
            row["ChapterName"] = chapterName;
            row["ReqAddress"] = reqUrl;
            row["RealAddress"] = mp3Url;
            row["ExtractorStatus"] = status;
            _novelData.Rows.Add(row);
        }

        public async Task<string> GetMp3UrlAsync(string url)
        {
            string audioSrc = "";
            var parentDocNode = GetDocumentNode(url);

            try
            {
                // 找到 <iframe> 标签
                var iframeNode = parentDocNode.QuerySelector("iframe");

                if (iframeNode != null)
                {
                    string iframeSrc = iframeNode.GetAttributeValue("src", string.Empty);

                    if (!string.IsNullOrEmpty(iframeSrc))
                    {
                        var iframeContent = await client.GetStringAsync(Site + iframeSrc);
                        audioSrc = GetAudioUrl(iframeContent);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return audioSrc;
        }

        private string GetAudioUrl(string str)
        {
            string[] arr;
            string murl = "";
            string murlKey = "";
            string murlVal = "";

            string url = "";
            string urlKey = "";
            string urlVal = "";
            string mp3 = "";
            string murlPtn = @"murl\w+ =.*";
            string urlPtn = @"(?<!m)url\w+ =.*";
            string mp3Ptn = "mp3:'.*";
            Match match;
            string newStr = "";

            match = Regex.Match(str, murlPtn);
            if (match != null)
            {
                murl = match.Value;
                murl = murl.Replace(";", "");
                murl = murl.Replace("'", "");
                murl = murl.Replace("\r", "");
                arr = murl.Split('=');
                murlKey = arr[0].Trim();
                murlVal = arr[1].Trim();
            }

            match = Regex.Match(str, urlPtn);
            if (match != null)
            {
                url = match.Value;
                url = url.Replace(";", "");
                url = url.Replace("'", "");
                url = url.Replace("\r", "");
                arr = url.Split('=');
                urlKey = arr[0].Trim();
                urlVal = arr[1].Trim();
            }

            if (murlVal == urlKey)
            {
                murlVal = urlVal;
            }
            if (urlVal == murlKey)
            {
                urlVal = murlVal;
            }

            match = Regex.Match(str, mp3Ptn);
            if (match != null)
            {
                mp3 = match.Value;
                mp3 = mp3.Remove(0, 4);
                arr = mp3.Split('+');

                for (int i = 0; i < arr.Length; i++)
                {
                    string tmp = arr[i].Trim();

                    if (tmp == "''")
                    {
                        continue;
                    }
                    if (tmp.StartsWith("'") && tmp.EndsWith("'"))
                    {
                        tmp = tmp.Replace("'", "");
                        newStr += tmp;
                    }
                    else
                    {
                        if (tmp == murlKey)
                        {
                            newStr += murlVal;
                        }
                        if (tmp == urlKey)
                        {
                            newStr += urlVal;
                        }
                    }
                }
            }

            return newStr;
        }
    }
}
